package app.server.project.core.websocket;

import app.server.project.core.constants.Def;
import app.server.project.core.constants.TcpCmd;
import app.server.project.core.exception.SysError;
import app.server.project.core.tcp.model.TcpRespModel;
import app.server.project.core.token.TokenCache;
import app.server.project.core.websocket.model.LoginReq;
import app.server.project.core.websocket.model.Online;
import app.server.project.core.websocket.model.PushUser;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
import app.server.project.core.websocket.model.WSOffline;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint("/ws")
@Slf4j
public class WSMgr {

    private final static int TIME_OUT = 30 * 1000;


    private ConcurrentHashMap<Long, PushUser> idMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<Session, PushUser> sessionMap = new ConcurrentHashMap<>();
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Autowired
    private TokenCache tokenCache;

    @OnOpen
    public void onOpen(Session session) {
        session.setMaxIdleTimeout(TIME_OUT);
    }

    @OnClose
    public void onClose(Session session) {
        PushUser pushUser = this.sessionMap.get(session);
        if (Objects.isNull(pushUser)) return;
        this.idMap.remove(pushUser.getUserId());
        this.sessionMap.remove(session);
        this.applicationEventPublisher.publishEvent(WSOffline.of(pushUser.getUserId()));
    }

    private void chkLogin(Session session, WSDataModel dataModel) {
        try {
            log.info("登录参数:{}",dataModel);
            var loginReq = LoginReq.of();
            BeanUtil.fillBeanWithMapIgnoreCase((Map<?, ?>) dataModel.getData(), loginReq, true);
            if (StrUtil.isBlank(loginReq.getToken())){
                session.getBasicRemote().sendText(JSONUtil.toJsonStr(WSDataModel.of(dataModel.getCmd(), SysError.Token)));
                log.info("登录错误,token为空");
                session.close();
                return;
            }
            var cache=this.tokenCache.getToken(loginReq.getUserId());
            log.info("缓存中的token:{}",cache);
            if (StrUtil.isBlank(cache)){
                session.getBasicRemote().sendText(JSONUtil.toJsonStr(WSDataModel.of(dataModel.getCmd(), SysError.EmptyToken)));
                log.info("登录错误,缓存不存在此token");
                session.close();
                return;
            }
            if (!cache.equals(loginReq.getToken())){
                session.getBasicRemote().sendText(JSONUtil.toJsonStr(WSDataModel.of(dataModel.getCmd(), SysError.ErrorToken)));
                log.info("登录错误,缓存中的token不相同");
                session.close();
                return;
            }
            var pushUser = PushUser.of(loginReq.getUserId(), session);
            this.idMap.put(loginReq.getUserId(), pushUser);
            this.sessionMap.put(session, pushUser);
            this.applicationEventPublisher.publishEvent(Online.of(pushUser.getUserId()));
        }catch (Exception e){

        }

    }

    private void doHeart(Session session, WSDataModel dataModel) {
        PushUser pushUser = this.sessionMap.get(session);
        if (Objects.isNull(pushUser)) return;
        pushUser.push(dataModel.getCmd(), null);
    }

    public void push(long id,int cmd, Object data) {
        var pushUser=this.idMap.get(id);
        if (Objects.isNull(pushUser))return;
        pushUser.push(cmd,data);
    }

    @OnMessage
    public void onMsg(Session session, String msg) {
        if (Objects.isNull(msg)) return;
        var data = JSONUtil.toBean(msg, WSDataModel.class);
        if (Objects.isNull(data)) return;
        switch (data.getCmd()) {
            case TcpCmd.Login: {
                this.chkLogin(session, data);
            }
            break;
            case TcpCmd.Heart: {
                this.doHeart(session, data);
            }
            break;
        }
    }

}
